// Written by Dale J Williams.
// Licensed under MPL 1.1 unless otherwise specified. 
// http://www.mozilla.org/MPL/MPL-1.1.html

package engineDJW.sound {
	
	
	/**
	 * SoundManager: Contains and controls game sounds, tracks.
	 * @author Dale J Williams
	 */
	public class SoundManager {
		
		private var _sounds		:Vector.<SoundObject>;
		private var _tracks		:Vector.<SoundTrack>;	
		private var _groups		:Vector.<SoundGroup>;	
		private var _default	:SoundTrack;
		
		private var _volume		:Number;
		
		public function SoundManager() {
			init();
		}
		
		private function init():void {
			_sounds = new Vector.<SoundObject>();
			_tracks = new Vector.<SoundTrack>();
			_groups = new Vector.<SoundGroup>();
			
			_default = new SoundTrack("default");
			addTrack(_default);
			
			_volume = 1;
		}
		
		public function playSound($soundOrSoundName:Object, $overrideTrackOrTrackName:Object = null, $overrideVolume:Number = -1):void {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object " + $soundOrSoundName + " is not a SoundObject or name.");
			var track:SoundTrack = $overrideTrackOrTrackName ? getTrackFromObject($overrideTrackOrTrackName) : sound.track;
			if (!track) 
				track = _default;
			if ($overrideVolume > 0) 
				track.volume = $overrideVolume;
			trace("playSound " + sound.name), 
			track.playSound(sound);
		}
		
		public function stopSound($soundOrSoundName:Object, $hardStop:Boolean=false):void {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object for stopping is not a SoundObject or name.");
			if (!sound.track) return;
			sound.track.stopSound(sound, $hardStop);
		}
		
		public function stopAllSounds($hardStop:Boolean = false):void {
			for each(var sound:SoundObject in _sounds)
				stopSound(sound);
		}
		
		public function addSound($sound:SoundObject, $trackOrTrackName:Object = null):SoundObject {
			var track:SoundTrack = getTrackFromObject($trackOrTrackName);
			if (!track) track = _default;
			$sound.track = track;
			_sounds.push($sound);
			return $sound;
		}
		
		public function removeSound($soundOrSoundName:Object):SoundObject {
			var sound:SoundObject = getSoundFromObject($soundOrSoundName);
			if (!sound) throw new Error("Object for removal is not a SoundObject or name.");
			var i:int = _sounds.length;
			while (i--) {
				if (_sounds[i] == sound)
					return _sounds.splice(i, 1)[0];
			}
			throw new Error("SoundManager does not contain specified sound.");
			return null;
		}
				
		public function getSound($soundName:String):SoundObject {
			var i:int = _sounds.length;
			while (i--) {
				if (_sounds[i].name == $soundName)
					return _sounds[i];
			}
			return null;
		}
		
		public function addTrack($trackOrTrackName:Object, $numChannels:int=-1, $fadeTime:Number=0, $volume:Number=1):SoundTrack {
			var track:SoundTrack;
			var trackName:String;
			if ($trackOrTrackName is SoundTrack) {
				track = $trackOrTrackName as SoundTrack;
				trackName = track.name;
			} else if ($trackOrTrackName is String) {
				trackName = $trackOrTrackName as String;
				track = new SoundTrack(trackName, $numChannels, $fadeTime, $volume);
			} else {
				throw new Error("Object " + $trackOrTrackName + " is not a SoundTrack or name.");			
			}
			if (!getTrack(trackName))
				_tracks.push(track);				
			track.manager = this;
			return track;
		}
		
		public function removeTrack($trackOrTrackName:Object):SoundTrack {
			var track:SoundTrack = getTrackFromObject($trackOrTrackName);
			if (!track) throw new Error("Object for removal is not a SoundTrack or name.");
			var i:int = _tracks.length;
			while (i--) {
				if (_tracks[i] == track) {
					_tracks.splice(i, 1)[0];
					track.stopAllSounds();
					track.manager = null;
					break;
				}
			}
			return track;
		}
		
		public function getTrack($trackName:String):SoundTrack {
			var i:int = _tracks.length;
			while (i--)
				if (_tracks[i].name == $trackName)
					return _tracks[i];
			return null;
		}
		
		public function addGroup($groupName:String):SoundGroup {
			var result:SoundGroup = getGroup($groupName);
			if (result) return result;
			result = new SoundGroup($groupName);
			_groups.push(result);
			return result;
		}
		
		public function addSoundToGroup($soundObjectOrName:Object, $groupName:String):void {
			var group:SoundGroup = getGroup($groupName);
			group.addSound(getSoundFromObject($soundObjectOrName));
		}
		
		public function playGroup($groupName:String):void {
			playSound(getGroup($groupName).getRandom());
		}
		
		public function getGroup($groupName:String):SoundGroup {
			var i:int = _groups.length;
			while (i--) {
				if (_groups[i].name == $groupName)
					return _groups[i];
			}
			return null;
		}
		
		private function getSoundFromObject($soundOrSoundName:Object):SoundObject {
			if ($soundOrSoundName is String)
				return getSound(String($soundOrSoundName));
			else if ($soundOrSoundName is SoundObject)
				return SoundObject($soundOrSoundName);
			return null;
		}
		
		private function getTrackFromObject($trackOrTrackName:Object):SoundTrack {
			if ($trackOrTrackName is String)
				return getTrack(String($trackOrTrackName));
			else if ($trackOrTrackName is SoundTrack)
				return SoundTrack($trackOrTrackName);
			return null;
		}
		
		private function updateVolume():void {
			for each(var track:SoundTrack in _tracks)
				track.updateVolume();
		}
		
		public function get volume():Number { return _volume; }		
		public function set volume(value:Number):void {
			_volume = value;
			updateVolume();
		}
		
	}

}